package cool.taomu.toolkit.kademlia

import com.google.common.base.Function
import cool.taomu.toolkit.inter.IFunction
import cool.taomu.toolkit.jersey.RequestDto
import cool.taomu.toolkit.kademlia.entity.KademliaNode
import cool.taomu.toolkit.kademlia.entity.KademliaReponse
import cool.taomu.toolkit.kademlia.entity.ServiceNode
import cool.taomu.toolkit.kademlia.service.KademliaSerivce
import cool.taomu.toolkit.kademlia.service.KademliaSerivce.findNodeEntity
import cool.taomu.toolkit.kademlia.thrift.KademliaService
import cool.taomu.toolkit.kademlia.thrift.KademliaService.Processor
import cool.taomu.toolkit.kademlia.thrift.Node
import cool.taomu.toolkit.kademlia.thrift.Response
import cool.taomu.toolkit.kademlia.thrift.ServiceEntity
import cool.taomu.toolkit.rpc.thrift.entity.ThriftConfig
import cool.taomu.toolkit.rpc.thrift.provider.ThritfTHshaClientProvider
import cool.taomu.toolkit.rpc.thrift.service.TaomuThriftTHsHaClient
import java.util.ArrayList
import java.util.List
import javax.inject.Inject
import javax.inject.Provider
import org.apache.commons.beanutils.BeanUtils
import org.apache.thrift.TException
import org.apache.thrift.TProcessor
import org.eclipse.xtend.lib.annotations.Accessors
import org.slf4j.LoggerFactory

@Deprecated
@Accessors
class KademliaSerivceImpl implements KademliaService.Iface {

	static class KademliaServiceProvider implements Provider<TProcessor> {
		@Inject
		KademliaSerivceImpl kademliaSerivceImpl

		override get() {
			new Processor(kademliaSerivceImpl);
		}

	}

	val static LOG = LoggerFactory.getLogger(KademliaSerivceImpl);

	@Inject
	KademliaSerivce service

	@Inject
	TaomuThriftTHsHaClient client;

	val Function<findNodeEntity, List<KademliaNode>> findNodeFunc = new Function<findNodeEntity, List<KademliaNode>>() {

		override apply(findNodeEntity input) {
			LOG.info("查找网络上的节点")
			var ThriftConfig config = new ThriftConfig();
			config.host = input.knode.host;
			config.port = input.knode.port;
			var ikey = #[config.host, config.port].join("#");
			if (!client.poolMap.containsKey(ikey)) {
				var pool = new ThritfTHshaClientProvider().getThriftClientPool(config.host, config.port, config.timeout)
				client.poolMap.put(ikey, pool)
			}
			val resNodes = new ArrayList<KademliaNode>();
			client.get(ikey, [ protocol |
				LOG.info("创建客户端");
				var client = new KademliaService.Client(protocol)
				var node = input.node.toNode
				LOG.info("执行findNode方法:{}\n==>{}",input.nodeId,node);
				var resp = client.findNodeAndNode(input.nodeId, node);
				if (resp.nodes !== null && resp.nodes.size > 0) {
					LOG.info("遍历find到的node:{}", resp.nodes.size);
					resp.nodes.forEach [
						LOG.info("将找到的节点添加到集合中");
						var KademliaNode knode = new KademliaNode(it);
						if (!resNodes.contains(knode)) {
							resNodes.add(knode)
						}
					]
				} else {
					LOG.info("未找到节点");
				}
			])
			return resNodes;
		}

	};

	Function<KademliaNode, Boolean> checkNode = new Function<KademliaNode, Boolean>() {

		override apply(KademliaNode knode) {
			LOG.info("检测节点:{}", knode)
			var ThriftConfig config = new ThriftConfig();
			config.host = knode.host;
			config.port = knode.port;
			var ikey = #[config.host, config.port].join("#");
			if (!client.poolMap.containsKey(ikey)) {
				var pool = new ThritfTHshaClientProvider().getThriftClientPool(config.host, config.port, config.timeout)
				client.poolMap.put(ikey, pool)
			}
			val res = new ArrayList<Boolean>(1);
			client.get(ikey, [ protocol |
				var client = new KademliaService.Client(protocol)
				var node = service.table.localNode.toNode;
				if (client.ping(node).result.equals("PONG")) {
					res.add(true);
				}
				res.add(false);
			]);
			if (res.size > 0) {
				return res.get(0);
			} else {
				return false;
			}
		}
	};

	override synchronized findNode(String nodeId) {
		this.findNodeAndNode(nodeId, null)
	}

	override synchronized findNodeAndNode(String nodeId, Node node) throws TException {
		LOG.info("执行findNode方法");
		var KademliaNode remoteNode = null;
		if (node !== null) {
			remoteNode = new KademliaNode(node.id, node.host, node.port, 0, node.timestamp);
		}
		var knodes = service.findNode(nodeId, remoteNode, findNodeFunc, checkNode)
		LOG.info("找到的节点数量:{}", knodes.size);
		val fnode = new ArrayList<Node>(knodes.size);
		knodes.forEach [ rnode |
			var nnode = rnode.toNode
			fnode.add(nnode)
		]
		LOG.info("返回找到的节点");
		return new Response(fnode, "", 200, "节点集合");
	}

	override synchronized findValue(String key) {
		this.findValueAndNode(key, null)
	}

	override synchronized findValueAndNode(String key, Node node) throws TException {
		LOG.info("执行findValue方法");
		var KademliaNode node2 = new KademliaNode(node);
		var kademliaReponse = service.findValue(key, node2, new Function<KademliaNode, List<KademliaReponse>>() {
			override apply(KademliaNode knode) {
				var ThriftConfig config = new ThriftConfig();
				config.host = knode.host;
				config.port = knode.port;
				var ikey = #[config.host, config.port].join("#");
				if (!client.poolMap.containsKey(ikey)) {
					var pool = new ThritfTHshaClientProvider().getThriftClientPool(config.host, config.port, config.timeout)
					client.poolMap.put(ikey, pool)
				}
				val resList = new ArrayList<KademliaReponse>();
				client.get(ikey, [ protocol |
					var client = new KademliaService.Client(protocol)
					var node = knode.toNode;
					var resp = client.findValueAndNode(key, node);
					LOG.info("*****执行远程节点的findValue {} 返回值 :,{}*****", key, resp);
					if (resp.status == 200) {
						LOG.info("*****resp.status 200 *****");
						var KademliaReponse kresp = new KademliaReponse(resp);
						resList.add(kresp);
					}
				])
				return resList;
			}
		}, checkNode)
		return kademliaReponse.toResponse;
	}

	override synchronized ping(Node node) throws TException {
		LOG.info("执行ping方法:{}", this.service === null);
		var KademliaNode knode = new KademliaNode(node);
		var kresponse = this.service.ping(knode, checkNode);
		var response = kresponse.toResponse
		response.message = "返回ping结果"
		return response;
	}

	override synchronized store(String key, String value) throws TException {
		LOG.info("执行store方法");
		service.store(key, value, findNodeFunc, new IFunction<ServiceNode>() {
			override apply(ServiceNode input) {
				var ThriftConfig config = new ThriftConfig();
				config.host = input.destNode.host;
				config.port = input.destNode.port;
				var ikey = #[config.host, config.port].join("#");
				if (!client.poolMap.containsKey(ikey)) {
					var pool = new ThritfTHshaClientProvider().getThriftClientPool(config.host, config.port, config.timeout)
					client.poolMap.put(ikey, pool)
				}
				client.get(ikey, [ protocol |
					var client = new KademliaService.Client(protocol)
					client.store(key, value);
				])
			}

		}, checkNode);
	}

	override findService(ServiceEntity serviceEntity) {
		this.findServiceAndNode(serviceEntity, null)
	}

	override findServiceAndNode(ServiceEntity serviceEntity, Node node) throws TException {
		LOG.info("执行findService方法:{}", serviceEntity);
		var KademliaNode node2 = null;
		if (node !== null) {
			node2 = new KademliaNode(node);
		}
		val dto = new RequestDto();
		BeanUtils.copyProperties(dto, serviceEntity)
		LOG.info("dto:{}", dto);
		var kademliaReponse = service.findService(dto, node2, new Function<KademliaNode, List<KademliaReponse>>() {
			override apply(KademliaNode knode) {
				var ThriftConfig config = new ThriftConfig();
				config.host = knode.host;
				config.port = knode.port;
				var ikey = #[config.host, config.port].join("#");
				if (!client.poolMap.containsKey(ikey)) {
					var pool = new ThritfTHshaClientProvider().getThriftClientPool(config.host, config.port, config.timeout)
					client.poolMap.put(ikey, pool)
				}
				val resList = new ArrayList<KademliaReponse>();
				client.get(ikey, [ protocol |
					var client = new KademliaService.Client(protocol)
					var node = knode.toNode;
					var resp = client.findServiceAndNode(serviceEntity, node);
					LOG.info("*****执行远程节点的findService {} 返回值 :,{}*****", dto.serviceName, resp);
					if (resp.status == 200) {
						LOG.info("*****resp.status 200 *****");
						var KademliaReponse kresp = new KademliaReponse(resp);
						resList.add(kresp);
					}
				])
				return resList;
			}
		}, checkNode)
		return kademliaReponse.toResponse;
	}
}
